import struct
import numpy as np
from collections import Counter, defaultdict
import pandas as pd
import xml.etree.ElementTree as ET
import os
import sys
import mmap
from typing import Generator, Optional, Callable, List, Dict, Any
import threading
from queue import Queue, Empty
import time
import gc
import logging
from frame_schema_parser import FrameSchemaParser
from logger import logger
from PyQt5.QtWidgets import QApplication


def get_resource_path(relative_path):
    """获取资源文件的绝对路径"""
    try:
        # PyInstaller创建临时文件夹,将路径存储在_MEIPASS中
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


class FrameProcessor:
    def __init__(self, schema_file: str):
        schema_file = get_resource_path(schema_file)
        self.schema = FrameSchemaParser(schema_file)
        self.frame_size = self.schema.get_total_length()
        self.logger = logging.getLogger('frame_analyzer')
        self.vc_counters = defaultdict(int)  # 为每个虚拟信道添加计数器
        self.frame_files = defaultdict(list)  # 存储具有相同帧计数的文件列表
        self.logger.info("初始化帧处理器，使用模式文件：%s，帧大小：%d字节", repr(schema_file), self.frame_size)

    def read_frame(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> list:
        """读取帧数据文件
        
        Args:
            file_path: 文件路径
            progress_callback: 进度回调函数，参数为进度百分比和状态描述
            
        Returns:
            list: 帧数据列表
        """
        try:
            self.logger.info("开始读取文件: %s", repr(file_path))
            file_ext = os.path.splitext(file_path)[1].lower()

            # 回调初始进度
            if progress_callback:
                progress_callback(0, f"准备读取文件 {os.path.basename(file_path)}...")

            if file_ext == '.xml':
                frames = self._read_xml_frame(file_path, progress_callback)
            elif file_ext == '.dat':
                frames = self._read_dat_frame(file_path, progress_callback)
            else:
                frames = self._read_raw_frame(file_path, progress_callback)

            self.logger.info("成功读取 %d 帧数据", len(frames))

            # 回调完成进度
            if progress_callback:
                progress_callback(100, f"文件读取完成，共 {len(frames)} 帧")

            return frames
        except Exception as e:
            self.logger.error("读取文件失败: %s", str(e))
            if progress_callback:
                progress_callback(0, f"读取文件失败: {str(e)}")
            raise

    def _read_xml_frame(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> list:
        """读取XML格式的帧数据文件"""
        try:
            if progress_callback:
                progress_callback(10, "解析XML文件...")

            frames = []
            tree = ET.parse(file_path)
            root = tree.getroot()

            # 获取总帧数以计算进度
            total_frames = len(root.findall('frame'))

            for i, frame_elem in enumerate(root.findall('frame')):
                frame = {}

                # 读取帧头
                sync_word = frame_elem.find('sync_word')
                frame['sync_word'] = int(sync_word.get('value'), 16)

                # 读取帧计数
                frame_count = frame_elem.find('frame_count')
                frame['frame_count'] = int(frame_count.get('value'))

                # 读取虚拟信道
                vc = frame_elem.find('virtual_channel')
                frame['virtual_channel'] = int(vc.get('value'))

                # 读取译码状态
                status = frame_elem.find('decode_status')
                frame['decode_status'] = int(status.get('value'))

                # 读取数据负载
                payload = frame_elem.find('payload')
                payload_data = payload.text.split()
                frame['payload'] = bytes([int(x, 16) for x in payload_data])

                frames.append(frame)

                # 更新进度
                if progress_callback and i % max(1, total_frames // 100) == 0:
                    progress = int(10 + (i / total_frames) * 80)
                    progress_callback(progress, f"已解析 {i}/{total_frames} 帧...")

            if progress_callback:
                progress_callback(90, "XML解析完成，正在处理数据...")

            return frames

        except Exception as e:
            self.logger.error(f"读取XML文件失败: {str(e)}")
            raise

    def _read_raw_frame(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> list:
        """读取RAW格式的帧数据文件"""
        try:
            frames = []
            file_size = os.path.getsize(file_path)
            total_frames = file_size // self.frame_size

            # 进度更新
            if progress_callback:
                progress_callback(5, f"开始读取文件，预计 {total_frames} 帧...")

            # 检查文件大小是否超过1GB
            if file_size > 1 * 1024 * 1024 * 1024:
                self.logger.info("检测到大文件(%.2f GB)，使用优化的大文件读取方法", file_size / (1024 * 1024 * 1024))
                if progress_callback:
                    progress_callback(10,
                                      f"检测到大文件(%.2f GB)，使用优化的方法..." % (file_size / (1024 * 1024 * 1024)))
                return self._read_large_file(file_path, progress_callback)

            with open(file_path, 'rb') as f:
                for i in range(total_frames):
                    frame_data = f.read(self.frame_size)
                    if not frame_data or len(frame_data) < self.frame_size:
                        break

                    frame = self._parse_frame(frame_data)
                    frames.append(frame)

                    # 定期更新进度
                    if i % 1000 == 0:
                        self.logger.debug(f"已读取 {i} 帧")
                        if progress_callback:
                            progress = int(10 + (i / total_frames) * 80)
                            progress_callback(progress, f"已读取 {i}/{total_frames} 帧...")

            if progress_callback:
                progress_callback(90, "文件读取完成，正在处理数据...")

            return frames

        except Exception as e:
            self.logger.error(f"读取RAW文件失败: {str(e)}")
            raise

    def _read_dat_frame(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> list:
        """读取DAT格式的帧数据文件
        
        DAT文件与RAW文件的读取方式相同，但可能有不同的内部结构
        """
        try:
            import numpy as np
            # 现有的1092字节帧格式
            FMT1092 = np.dtype([
                ('sync_word', '>u4'),
                ('length', '>u4'),
                ('zero', '>u4'),
                ('t1', '>u4'),
                ('t2', '>u4'),
                ('frame_count', '>u4'),
                ('帧检验结果', '>u4'),
                ('帧同步状态', '>u4'),
                ('位滑状态', '>u4'),  # 0：无位滑；1：有位滑。
                ('virtual_channel', '>u4'),  # 借用空闲字段 存放 数据虚拟信道标识
                ('frame_len', '>u4'),
                ('同步字长度', '>u4'),
                ('decode_status', '>u4'),  # 译码状态
                ('同步字状态', '>u4'),
                ('f_001', '>u4', 2),
                ('f896', [
                    ('sync', '>u4'),
                    ('h1', '>u2'),
                    ('h2', '>u4'),
                    ('payload', 'S886')
                ]),
                ('f128', 'V128'),
                ('synn_end', '>u4'),
            ])
            
            # 添加1024字节帧格式支持
            FMT1024 = np.dtype([
                ('sync_word', '>u4'),        # 同步头 0x1ACFFC1D
                ('version_scid', '>u1'),     # 版本号(高2位)+航天器标识(低6位)
                ('vcid', '>u1'),             # 虚拟信道标识(低6位)
                ('vc_count', 'V3'),          # 虚拟信道帧计数（3字节）
                ('flag', '>u1'),             # 标志域
                ('insert_zone', 'V8'),       # 插入域
                ('data_zone', 'V878'),       # 数据域
                ('rs_check', 'V128')         # RS校验码
            ])
            
            # 读取数据文件
            file_size = os.path.getsize(file_path)
            if progress_callback:
                progress_callback(10, f"读取DAT文件，大小 {file_size / (1024 * 1024):.2f} MB...")
            
            # 先尝试读取文件前4个字节判断帧格式
            with open(file_path, 'rb') as f:
                header_bytes = f.read(4)
                f.seek(0)  # 重置文件指针
                
                # 判断帧头类型
                is_1024_format = False
                if header_bytes == b'\x1A\xCF\xFC\x1D':
                    is_1024_format = True
                    self.logger.info("检测到1024字节帧格式 (帧头: 0x1ACFFC1D)")
                    if progress_callback:
                        progress_callback(15, "检测到1024字节帧格式，使用对应解析方式...")
                
                # 读取原始帧数据用于构造正确的payload
                f.seek(0)
                raw_data = f.read()
                
                # 根据帧格式选择适当的结构
                if is_1024_format and file_size % 1024 == 0:
                    # 使用1024字节格式解析
                    data = np.fromfile(file_path, dtype=FMT1024)
                    if progress_callback:
                        progress_callback(50, f"已读取{len(data)}帧数据(1024字节格式)，正在处理...")
                else:
                    # 使用1092字节格式解析
                    data = np.fromfile(file_path, dtype=FMT1092)
                    if progress_callback:
                        progress_callback(50, f"已读取{len(data)}帧数据(1092字节格式)，正在处理...")
            
            frames = []
            
            # 根据不同格式处理数据
            if is_1024_format and file_size % 1024 == 0:
                # 处理1024字节格式
                for i, frame in enumerate(data):
                    # 更新进度
                    if progress_callback and i % max(1, len(data) // 20) == 0:
                        progress = int(50 + (i / len(data) * 40))
                        progress_callback(progress, f"正在处理第 {i+1}/{len(data)} 帧(1024字节格式)...")
                    
                    # 提取虚拟信道ID
                    vcid = int(frame['vcid']) & 0x3F  # 取低6位
                    
                    # 提取虚拟信道帧计数
                    vc_count_bytes = frame['vc_count'].tobytes()
                    vc_count = int.from_bytes(vc_count_bytes, byteorder='big')
                    
                    # 直接用原始帧数据作为payload（1024字节帧全部数据）
                    frame_start = i * 1024
                    payload = raw_data[frame_start:frame_start + 1024]
                    
                    # 确定译码状态 - 对于1024字节格式，我们默认用0表示无译码状态
                    # 检查RS校验码是否全为0，如果是可能表示校验失败
                    rs_check_bytes = np.frombuffer(frame['rs_check'], dtype=np.uint8)
                    rs_check_sum = np.sum(rs_check_bytes)
                    decode_status = 0
                    if rs_check_sum > 0:
                        decode_status = 1  # 表示有RS校验
                    
                    # 计算帧检验结果 - 这里简化处理，实际应根据RS校验结果设置
                    frame_check_result = 0
                    if rs_check_sum > 0:
                        # RS校验码存在，但没有实际验证结果
                        frame_check_result = (0 << 24)  # 假设校验正确
                    
                    frames.append({
                        'sync_word': int(frame['sync_word']),
                        'frame_count': vc_count,  # 使用虚拟信道帧计数作为帧计数
                        'virtual_channel': vcid,
                        'decode_status': decode_status,
                        'payload': payload,  # 直接用原始帧数据
                        'frame_len': 1024,
                        'frame_sync_status': 1,  # 假设同步状态良好
                        'bit_slip_status': 0,  # 假设无位滑
                        '帧检验结果': frame_check_result,
                        '同步字状态': 0
                    })
            else:
                # 处理1092字节格式
                # 设置虚拟信道值
                data['virtual_channel'] = data['f896']['h1'] & 0x3F  # 借用空闲字段存放数据虚拟信道标识
                
                for i, frame in enumerate(data):
                    # 更新进度
                    if progress_callback and i % max(1, len(data) // 20) == 0:
                        progress = int(50 + (i / len(data) * 40))
                        progress_callback(progress, f"正在处理第 {i+1}/{len(data)} 帧(1092字节格式)...")
    
                    # 直接用原始帧数据的第64-1087字节作为payload（VCDU数据区）
                    # 这样payload[6:9]就是原始帧的第70-72字节，与test_analyze.py完全一致
                    frame_start = i * 1092
                    payload = raw_data[frame_start + 64:frame_start + 1088]  # 第64-1087字节，共1024字节
    
                    # 将NumPy标量转换为Python原生类型
                    frames.append({
                        'sync_word': int(frame['sync_word']),
                        'frame_count': int(frame['frame_count']),
                        'virtual_channel': int(frame['virtual_channel']),
                        'decode_status': int(frame['decode_status']),
                        'payload': payload,  # 直接用原始帧数据的VCDU区
                        'frame_len': int(frame['frame_len']),
                        'frame_sync_status': int(frame['帧同步状态']),
                        'bit_slip_status': int(frame['位滑状态']),
                        '帧检验结果': int(frame['帧检验结果']),
                        '同步字状态': int(frame['同步字状态'])
                    })

            if progress_callback:
                progress_callback(95, f"处理完成，共读取 {len(frames)} 帧")
                
            return frames

        except Exception as e:
            self.logger.error(f"读取DAT文件失败: {str(e)}")
            raise

    def _read_large_file(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> list:
        """优化的大文件读取方法，使用内存映射和流式处理
        
        适用于超过1GB的大文件，最大支持2GB
        使用内存映射和分批处理来降低内存占用
        """
        try:
            self.logger.info("使用内存映射方式读取大文件: %s", repr(file_path))
            file_size = os.path.getsize(file_path)

            if file_size > 2 * 1024 * 1024 * 1024:
                self.logger.warning("文件大小超过2GB(%.2f GB)，可能导致处理缓慢", file_size / (1024 * 1024 * 1024))
                if progress_callback:
                    progress_callback(10, f"警告：文件大小超过2GB(%.2f GB)，处理可能较慢..." % (
                                file_size / (1024 * 1024 * 1024)))

            total_frames = file_size // self.frame_size
            # 调整批处理大小，根据文件大小动态设置，越大的文件批次越小
            # 这样可以更频繁地更新进度条和释放内存
            if file_size > 1.5 * 1024 * 1024 * 1024:  # 超过1.5GB
                batch_size = 1000
            elif file_size > 1 * 1024 * 1024 * 1024:  # 超过1GB
                batch_size = 2000
            else:
                batch_size = 5000  # 默认批次大小

            self.logger.info(f"使用批处理大小: {batch_size}")

            frames = []
            total_batches = (total_frames + batch_size - 1) // batch_size

            if progress_callback:
                progress_callback(15, f"开始分批处理大文件，总共 {total_batches} 批...",
                                  f"文件大小: {file_size / (1024 * 1024):.2f} MB")

            with open(file_path, 'rb') as f:
                # 使用内存映射提高性能
                with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
                    last_ui_update_time = time.time()

                    for batch_idx, batch_start in enumerate(range(0, total_frames, batch_size)):
                        batch_end = min(batch_start + batch_size, total_frames)
                        self.logger.debug(f"处理批次 {batch_idx + 1}/{total_batches}，帧 {batch_start}-{batch_end - 1}")

                        if progress_callback:
                            batch_progress = int(20 + (batch_idx / total_batches) * 70)
                            progress_callback(batch_progress, f"处理批次 {batch_idx + 1}/{total_batches}",
                                              f"帧 {batch_start}-{batch_end - 1}")

                            # 允许UI处理事件
                            current_time = time.time()
                            if current_time - last_ui_update_time > 0.1:  # 最多每100ms更新一次UI
                                QApplication.processEvents()
                                last_ui_update_time = current_time

                        batch_frames = []
                        for i in range(batch_start, batch_end):
                            offset = i * self.frame_size

                            # 确保不超出文件大小
                            if offset + self.frame_size > file_size:
                                self.logger.warning(f"文件末尾处数据不完整，跳过帧 {i}")
                                break

                            frame_data = mm[offset:offset + self.frame_size]

                            if not frame_data or len(frame_data) < self.frame_size:
                                self.logger.warning(f"帧 {i} 数据不完整，跳过")
                                continue

                            frame = self._parse_frame(frame_data)
                            batch_frames.append(frame)

                            # 每处理一定数量的帧更新一次进度并刷新UI
                            # 对于大文件要更频繁地更新，提高响应性
                            update_interval = max(100, batch_size // 20)  # 每批次至少更新20次

                            if progress_callback and (i - batch_start) % update_interval == 0 and i > batch_start:
                                inner_progress = int(20 + (batch_idx / total_batches) * 70 +
                                                     ((i - batch_start) / batch_size) * (70 / total_batches))
                                progress_callback(inner_progress,
                                                  f"批次 {batch_idx + 1}/{total_batches}",
                                                  f"处理帧 {i - batch_start}/{batch_end - batch_start}")

                                # 每次进度更新时刷新UI
                                QApplication.processEvents()
                                last_ui_update_time = time.time()

                        frames.extend(batch_frames)

                        # 强制垃圾回收以释放内存
                        del batch_frames
                        gc.collect()

                        # 每批次结束后强制刷新UI
                        QApplication.processEvents()

                        self.logger.debug(f"已处理 {len(frames)}/{total_frames} 帧")

            if progress_callback:
                progress_callback(90, f"文件读取完成", f"共处理 {len(frames)} 帧，正在最终处理...")

            return frames

        except Exception as e:
            self.logger.error(f"读取大文件失败: {str(e)}")
            raise

    def read_frame_generator(self, file_path: str, progress_callback: Optional[Callable[[int, str], None]] = None) -> \
            Generator[dict, None, None]:
        """使用生成器模式读取帧，适用于超大文件的流式处理
        
        返回一个生成器而不是完整的帧列表，可以逐帧处理而不消耗大量内存
        """
        try:
            self.logger.info("开始使用生成器模式读取文件: %s", repr(file_path))
            file_size = os.path.getsize(file_path)
            total_frames = file_size // self.frame_size

            if progress_callback:
                progress_callback(5, f"开始使用流式处理方式读取文件，预计 {total_frames} 帧...")

            with open(file_path, 'rb') as f:
                # 对于超大文件使用内存映射
                if file_size > 1 * 1024 * 1024 * 1024:
                    with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
                        for i in range(total_frames):
                            offset = i * self.frame_size
                            frame_data = mm[offset:offset + self.frame_size]

                            if not frame_data or len(frame_data) < self.frame_size:
                                break

                            if progress_callback and i % 1000 == 0:
                                progress = int(10 + (i / total_frames) * 85)
                                progress_callback(progress, f"已处理 {i}/{total_frames} 帧...")

                            yield self._parse_frame(frame_data)

                            if i % 10000 == 0:
                                gc.collect()  # 定期垃圾回收
                else:
                    # 对于小文件，使用标准读取方式
                    for i in range(total_frames):
                        frame_data = f.read(self.frame_size)
                        if not frame_data or len(frame_data) < self.frame_size:
                            break

                        if progress_callback and i % 1000 == 0:
                            progress = int(10 + (i / total_frames) * 85)
                            progress_callback(progress, f"已处理 {i}/{total_frames} 帧...")

                        yield self._parse_frame(frame_data)

            if progress_callback:
                progress_callback(95, "文件读取完成")

        except Exception as e:
            self.logger.error(f"生成器读取文件失败: {str(e)}")
            raise

    def _parse_frame(self, frame_data: bytes) -> dict:
        """解析单个帧数据"""
        try:
            # 解析帧头
            sync_word = struct.unpack('>I', frame_data[0:4])[0]
            frame_count = struct.unpack('>I', frame_data[4:8])[0]
            virtual_channel = frame_data[8]
            decode_status = frame_data[9]
            payload = frame_data[10:]

            frame = {
                'sync_word': sync_word,
                'frame_count': frame_count,
                'virtual_channel': virtual_channel,
                'decode_status': decode_status,
                'payload': payload
            }

            # 记录详细的帧信息
            self.logger.debug(
                f"解析帧数据：\n"
                f"  - 同步字：0x{sync_word:08X}\n"
                f"  - 帧计数：{frame_count}\n"
                f"  - 虚拟信道：{virtual_channel} (0x{virtual_channel:02X})\n"
                f"  - 译码状态：{decode_status} (0x{decode_status:02X})\n"
                f"  - 负载长度：{len(payload)}字节"
            )

            # 解析帧检验&译码状态
            if len(payload) >= 52:
                frame_check_decode = int.from_bytes(payload[48:52], byteorder='big')
                frame_check_result = int.from_bytes(payload[24:28], byteorder='big')

                # 获取帧检验&译码状态的Bit 0和Bit 4
                bit0_value = frame_check_decode & 0x01
                bit4_value = (frame_check_decode >> 4) & 0x01

                decode_info = []
                if frame_check_decode == 0:
                    decode_info.append("无R-S译码")
                else:
                    if bit0_value == 1:
                        rs_error_bit = (frame_check_result >> 24) & 0x01
                        decode_info.append(f"RS译码{'错误' if rs_error_bit == 1 else '正确'}")

                    if bit4_value == 1:
                        ldpc_status_byte = (frame_check_result >> 24) & 0xFF
                        if ldpc_status_byte == 0x00:
                            decode_info.append("LDPC译码正确")
                        elif ldpc_status_byte == 0x01:
                            decode_info.append("LDPC译码失败")
                        else:
                            decode_info.append(f"LDPC状态未知(0x{ldpc_status_byte:02X})")

                self.logger.debug(
                    f"帧检验&译码状态详情：\n"
                    f"  - 帧检验&译码状态值：0x{frame_check_decode:08X}\n"
                    f"  - RS译码使能：{'是' if bit0_value == 1 else '否'}\n"
                    f"  - LDPC译码使能：{'是' if bit4_value == 1 else '否'}\n"
                    f"  - 译码状态：{', '.join(decode_info)}"
                )

            return frame

        except Exception as e:
            self.logger.error(f"解析帧数据失败: {str(e)}", exc_info=True)
            raise

    def analyze_frames(self, frames, file_path: str = None) -> dict:
        """分析帧数据"""
        try:
            self.logger.info(f"analyze_frames 被调用，frames 类型: {type(frames)}, 长度: {len(frames) if hasattr(frames, '__len__') else 'unknown'}")
            
            # 处理不同的输入数据格式（NumPy数组或普通列表）
            if isinstance(frames, list) and len(frames) == 1 and hasattr(frames[0], 'size'):
                # NumPy数组格式 - 但我们已经在 _read_dat_frame 中修正了 payload，
                # 所以这里应该不会进入，因为 _read_dat_frame 返回的是标准列表
                self.logger.warning("检测到NumPy数组格式，但应该使用标准列表格式以确保 payload 正确")
                try:
                    return self._analyze_numpy_frames(frames[0], file_path)
                except Exception as numpy_error:
                    self.logger.warning("NumPy帧分析失败，尝试使用标准方法: %s", str(numpy_error))
                    # 如果NumPy分析失败，尝试转换为标准列表处理
                    try:
                        std_frames = [dict(frame) for frame in frames[0]]
                        return self._analyze_standard_frames(std_frames, file_path)
                    except Exception as convert_error:
                        self.logger.error("NumPy帧转换失败: %s", str(convert_error))
                        # 如果转换也失败，继续抛出原错误
                        raise numpy_error
            
            # 标准Python列表格式 - 这是我们修正后的主要路径
            self.logger.info("使用标准列表格式进行分析（payload 已修正）")
            return self._analyze_standard_frames(frames, file_path)

        except Exception as e:
            self.logger.error("分析帧数据失败: %s", str(e), exc_info=True)
            raise
            
    def _analyze_numpy_frames(self, numpy_frames, file_path: str = None) -> dict:
        """分析NumPy格式的帧数据
        
        专为处理NumPy数组格式的帧优化，避免类型转换和循环
        """
        try:
            self.logger.info("开始分析帧数据，总帧数：%d (NumPy格式)", numpy_frames.size)

            total_frames_count = numpy_frames.size
            
            # 安全地获取NumPy数组字段，并转换为Python类型
            try:
                virtual_channel = numpy_frames['virtual_channel']
                virtual_channels_counter = Counter(numpy_frames['virtual_channel'])
                frame_count = numpy_frames['frame_count']
                decode_status = numpy_frames['decode_status']
                decode_status_counter = Counter(decode_status)
            except (KeyError, TypeError) as field_error:
                self.logger.error("NumPy帧缺少必要字段: %s", str(field_error))
                raise
                
            # 基础统计信息
            stats = {
                'total_frames': total_frames_count,
                'virtual_channels': {int(vc): int(count) for vc, count in virtual_channels_counter.items()},
                'decode_status': {int(status): int(count) for status, count in decode_status_counter.items()},
                'idle_frames': int(virtual_channels_counter.get(0x3F, 0)),
                'vc_counters': {},
                'field_descriptions': {
                    'virtual_channels': {},
                    'decode_status': {}
                },
                'detailed_stats': {
                    'frame_count_stats': {
                        'min': int(np.min(frame_count)) if total_frames_count > 0 else 0,
                        'max': int(np.max(frame_count)) if total_frames_count > 0 else 0,
                        'continuous_ratio': 0.0,  # 先初始化为0，后面再计算
                        'continuous_info': {'continuous_ratio': 0.0, 'discontinuous_frames': [], 'total_gaps': 0}  # 初始化连续性详细信息
                    },
                    'virtual_channel_stats': {
                        'active_channels': 0,
                        'idle_ratio': 0,
                        'channel_distribution': {}
                    },
                    'decode_status_stats': {
                        'success_ratio': 0,
                        'error_ratio': 0
                    }
                },
                'frame_analysis': {}
            }
            
            # 安全计算连续性
            try:
                # 采样以提高性能
                if total_frames_count > 1000:
                    # 采样1000个点进行分析
                    indices = np.linspace(0, total_frames_count-1, 1000, dtype=int)
                    sampled_frame_counts = []
                    # 创建采样的帧数据列表，包含虚拟信道信息和payload
                    sampled_frames = []
                    for i in indices:
                        # 获取payload数据
                        if 'f896' in numpy_frames.dtype.names:
                            # 1092字节格式，需要构造完整的VCDU数据区
                            vcdu_data = (
                                struct.pack('>I', int(numpy_frames[i]['f896']['sync'])) +      # 第64-67字节：帧同步头
                                struct.pack('>H', int(numpy_frames[i]['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                                struct.pack('>I', int(numpy_frames[i]['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                                numpy_frames[i]['f896']['payload'].tobytes()                   # 第74-1091字节：数据
                            )
                            payload = vcdu_data
                        else:
                            # 其他格式，尝试获取payload
                            payload = numpy_frames[i].get('payload', b'') if hasattr(numpy_frames[i], 'get') else b''
                        
                        sampled_frames.append({
                            'frame_count': int(frame_count[i]),
                            'virtual_channel': int(virtual_channel[i]),
                            'payload': payload
                        })
                        
                        # 从payload中提取虚拟信道帧计数
                        if isinstance(payload, np.ndarray):
                            payload_bytes = payload.tobytes()
                        else:
                            payload_bytes = payload
                            
                        if len(payload_bytes) >= 1024:  # 1092字节帧的VCDU数据区（1024字节）
                            # 虚拟信道帧计数在第6-8字节（相对于VCDU数据区开始）
                            vc_count = int.from_bytes(payload_bytes[6:9], byteorder='big') if len(payload_bytes) >= 9 else 0
                        elif len(payload_bytes) >= 896:  # 1024或896字节帧
                            # 虚拟信道帧计数在第6-8字节
                            vc_count = int.from_bytes(payload_bytes[6:9], byteorder='big') if len(payload_bytes) >= 9 else 0
                        else:
                            vc_count = 0  # 无法确定帧类型
                        sampled_frame_counts.append(vc_count)
                else:
                    sampled_frame_counts = []
                    # 创建完整的帧数据列表
                    sampled_frames = []
                    for i in range(total_frames_count):
                        # 获取payload数据
                        if 'f896' in numpy_frames.dtype.names:
                            # 1092字节格式，需要构造完整的VCDU数据区
                            vcdu_data = (
                                struct.pack('>I', int(numpy_frames[i]['f896']['sync'])) +      # 第64-67字节：帧同步头
                                struct.pack('>H', int(numpy_frames[i]['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                                struct.pack('>I', int(numpy_frames[i]['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                                numpy_frames[i]['f896']['payload'].tobytes()                   # 第74-1091字节：数据
                            )
                            payload = vcdu_data
                        else:
                            # 其他格式，尝试获取payload
                            payload = numpy_frames[i].get('payload', b'') if hasattr(numpy_frames[i], 'get') else b''
                        
                        sampled_frames.append({
                            'frame_count': int(frame_count[i]),
                            'virtual_channel': int(virtual_channel[i]),
                            'payload': payload
                        })
                        
                        # 从payload中提取虚拟信道帧计数
                        if isinstance(payload, np.ndarray):
                            payload_bytes = payload.tobytes()
                        else:
                            payload_bytes = payload
                            
                        if len(payload_bytes) >= 1024:  # 1092字节帧的VCDU数据区（1024字节）
                            # 虚拟信道帧计数在第6-8字节（相对于VCDU数据区开始）
                            vc_count = int.from_bytes(payload_bytes[6:9], byteorder='big') if len(payload_bytes) >= 9 else 0
                        elif len(payload_bytes) >= 896:  # 1024或896字节帧
                            # 虚拟信道帧计数在第6-8字节
                            vc_count = int.from_bytes(payload_bytes[6:9], byteorder='big') if len(payload_bytes) >= 9 else 0
                        else:
                            vc_count = 0  # 无法确定帧类型
                        sampled_frame_counts.append(vc_count)
                        
                continuous_info_sample = self._calculate_continuous_info(sampled_frames)    
                stats['detailed_stats']['frame_count_stats']['continuous_ratio'] = continuous_info_sample['continuous_ratio']
                stats['detailed_stats']['frame_count_stats']['continuous_info'] = continuous_info_sample
            except Exception as cont_error:
                self.logger.warning("计算帧连续性失败: %s", str(cont_error))
                stats['detailed_stats']['frame_count_stats']['continuous_ratio'] = 0.0
                stats['detailed_stats']['frame_count_stats']['continuous_info'] = {
                    'continuous_ratio': 0.0,
                    'discontinuous_frames': [],
                    'total_gaps': 0
                }
            
            # VC计数器
            stats['vc_counters'] = {int(vc): int(count) for vc, count in virtual_channels_counter.items() if vc != 0x3F}
            self.vc_counters = stats['vc_counters']

            # 更新详细统计信息
            active_channels = len([vc for vc in stats['virtual_channels'].keys() if vc != 0x3F])
            idle_ratio = (stats['idle_frames'] / stats['total_frames'] * 100) if stats['total_frames'] > 0 else 0

            stats['detailed_stats']['virtual_channel_stats'].update({
                'active_channels': active_channels,
                'idle_ratio': idle_ratio,
                'channel_distribution': {int(vc): int(count) for vc, count in virtual_channels_counter.items()}
            })

            # 更新译码状态统计
            success_frames = sum(count for status, count in decode_status_counter.items() if status != 0)
            error_frames = decode_status_counter.get(0, 0)

            stats['detailed_stats']['decode_status_stats'].update({
                'success_ratio': (success_frames / total_frames_count * 100) if total_frames_count > 0 else 0,
                'error_ratio': (error_frames / total_frames_count * 100) if total_frames_count > 0 else 0
            })

            # 更新帧分析信息
            try:
                frame_count_counter = Counter(int(fc) for fc in frame_count)
                stats['frame_analysis'] = {
                    'frame_count_distribution': {int(fc): int(count) for fc, count in frame_count_counter.items()},
                    'frame_count_files': dict(self.frame_files),
                    'vc_payload_stats': {int(vc): {'total_bytes': int(count) * 1024, 'counter': int(count)}
                                       for vc, count in stats['virtual_channels'].items()}
                }
            except Exception as frame_error:
                self.logger.warning("帧计数分析失败: %s", str(frame_error))
                stats['frame_analysis'] = {
                    'frame_count_distribution': {},
                    'frame_count_files': dict(self.frame_files),
                    'vc_payload_stats': {}
                }
                
            # 如果提供了文件路径，记录文件关联
            if file_path:
                try:
                    unique_frame_counts = np.unique(frame_count)
                    for fc in unique_frame_counts:
                        fc_int = int(fc)
                        if fc_int not in self.frame_files:
                            self.frame_files[fc_int] = []
                        if file_path not in self.frame_files[fc_int]:
                            self.frame_files[fc_int].append(file_path)
                except Exception as file_error:
                    self.logger.warning("记录帧计数文件关联失败: %s", str(file_error))

            self.logger.info(
                "帧数据分析完成，统计结果：\n"
                "  - 总帧数：%d\n"
                "  - 空闲帧数：%d\n"
                "  - 活动虚拟信道数：%d\n"
                "  - 不同译码状态数：%d",
                stats['total_frames'], stats['idle_frames'],
                stats['detailed_stats']['virtual_channel_stats']['active_channels'], 
                len(stats['decode_status'])
            )

            return stats
        except Exception as e:
            self.logger.error("NumPy帧分析失败: %s", str(e), exc_info=True)
            raise
            
    def _analyze_standard_frames(self, frames, file_path: str = None) -> dict:
        """分析标准列表格式的帧数据"""
        try:
            self.logger.info("开始分析帧数据，总帧数：%d (标准列表格式)", len(frames))
            
            # 验证第一帧的 payload 是否正确（调试用）
            if frames and len(frames) > 0:
                first_frame = frames[0]
                payload = first_frame.get('payload', b'')
                if len(payload) >= 9:
                    vc_count_from_payload = int.from_bytes(payload[6:9], byteorder='big')
                    self.logger.info(f"第一帧 payload[6:9] 的虚拟信道帧计数: {vc_count_from_payload} (0x{vc_count_from_payload:06X})")
                    # 输出 payload 前16字节用于调试
                    hex_dump = ' '.join(f'{b:02X}' for b in payload[:16]) if len(payload) >= 16 else ' '.join(f'{b:02X}' for b in payload)
                    self.logger.info(f"第一帧 payload 前16字节: {hex_dump}")

            # 专门打印第333帧的详细信息（和 test_analyze.py 对比）
            if len(frames) > 333:
                frame_333 = frames[333]
                payload_333 = frame_333.get('payload', b'')
                self.logger.info(f"=== 第333帧详细信息（和 test_analyze.py 对比）===")
                self.logger.info(f"  virtual_channel: {frame_333['virtual_channel']}")
                self.logger.info(f"  decode_status  : {frame_333['decode_status']:02X}")
                self.logger.info(f"  frame_sync_status: {frame_333.get('frame_sync_status', 'N/A')}")
                self.logger.info(f"  bit_slip_status : {frame_333.get('bit_slip_status', 'N/A')}")
                
                if len(payload_333) >= 10:
                    # h2(raw) - payload[6:10] 对应原始帧的第70-73字节
                    h2_raw = ' '.join(f'{b:02X}' for b in payload_333[6:10])
                    self.logger.info(f"  h2(raw)         : {h2_raw}")
                    
                    # VCF_CNT(6-8) - payload[6:9] 对应原始帧的第70-72字节
                    vcf_cnt_6_8 = ' '.join(f'{b:02X}' for b in payload_333[6:9])
                    self.logger.info(f"  VCF_CNT(6-8)    : {vcf_cnt_6_8}")
                    
                    # VCF_CNT(7-9) - payload[7:10] 对应原始帧的第71-73字节
                    if len(payload_333) >= 10:
                        vcf_cnt_7_9 = ' '.join(f'{b:02X}' for b in payload_333[7:10])
                        self.logger.info(f"  VCF_CNT(7-9)    : {vcf_cnt_7_9}")
                
                # 输出整个 payload 的前30字节用于全面对比
                if len(payload_333) >= 30:
                    payload_hex = ' '.join(f'{b:02X}' for b in payload_333[:30])
                    self.logger.info(f"  payload前30字节 : {payload_hex}")
                
                self.logger.info(f"=======================================")

            total_frames_count = len(frames)
            virtual_channels_counter = Counter(frame['virtual_channel'] for frame in frames)
            
            # 基础统计信息
            stats = {
                'total_frames': total_frames_count,
                'virtual_channels': dict(virtual_channels_counter),
                'decode_status': {},
                'idle_frames': 0,
                'vc_counters': {},
                'field_descriptions': {
                    'virtual_channels': {},
                    'decode_status': {}
                },
                'detailed_stats': {
                    'frame_count_stats': {
                        'min': min(frame['frame_count'] for frame in frames) if frames else 0,
                        'max': max(frame['frame_count'] for frame in frames) if frames else 0,
                        'continuous_ratio': 0.0,  # 先初始化，后面计算
                        'continuous_info': {}  # 先初始化，后面计算
                    },
                    'virtual_channel_stats': {
                        'active_channels': 0,
                        'idle_ratio': 0,
                        'channel_distribution': {}
                    },
                    'decode_status_stats': {
                        'success_ratio': 0,
                        'error_ratio': 0
                    }
                },
                'frame_analysis': {}
            }

            self.logger.info("开始处理帧数据")

            # 计算译码状态统计
            decode_status_counter = Counter(frame['decode_status'] for frame in frames)
            stats['decode_status'] = dict(decode_status_counter)
            stats['idle_frames'] = virtual_channels_counter.get(0x3F, 0)
            
            # 处理虚拟信道计数
            vc_counters = {}
            for frame in frames:
                vc = frame['virtual_channel']
                if vc != 0x3F:  # 排除空闲帧
                    vc_counters[vc] = vc_counters.get(vc, 0) + 1
                    
            stats['vc_counters'] = vc_counters
            self.vc_counters = vc_counters

            # 计算连续性信息 - 直接用全部帧数据，按虚拟信道分组分析
            self.logger.info("开始计算帧连续性信息（按虚拟信道分组）")
            continuous_info = self._calculate_continuous_info(frames)
            stats['detailed_stats']['frame_count_stats']['continuous_info'] = continuous_info
            stats['detailed_stats']['frame_count_stats']['continuous_ratio'] = continuous_info['continuous_ratio']

            # 更新详细统计信息
            active_channels = len([vc for vc in stats['virtual_channels'].keys() if vc != 0x3F])
            idle_ratio = (stats['idle_frames'] / stats['total_frames'] * 100) if stats['total_frames'] > 0 else 0

            stats['detailed_stats']['virtual_channel_stats'].update({
                'active_channels': active_channels,
                'idle_ratio': idle_ratio,
                'channel_distribution': dict(virtual_channels_counter)
            })

            # 更新译码状态统计
            success_frames = sum(count for status, count in decode_status_counter.items() if status != 0)
            error_frames = decode_status_counter.get(0, 0)

            stats['detailed_stats']['decode_status_stats'].update({
                'success_ratio': (success_frames / total_frames_count * 100) if total_frames_count > 0 else 0,
                'error_ratio': (error_frames / total_frames_count * 100) if total_frames_count > 0 else 0
            })

            # 更新帧分析信息
            frame_count_counter = Counter(frame['frame_count'] for frame in frames)
            stats['frame_analysis'] = {
                'frame_count_distribution': dict(frame_count_counter),
                'frame_count_files': dict(self.frame_files),
                'vc_payload_stats': {vc: {'total_bytes': count * 1024, 'counter': count}
                                   for vc, count in stats['virtual_channels'].items()}
            }
            
            # 如果提供了文件路径，记录文件关联
            if file_path:
                for frame in frames:
                    fc = frame['frame_count']
                    if fc not in self.frame_files:
                        self.frame_files[fc] = []
                    if file_path not in self.frame_files[fc]:
                        self.frame_files[fc].append(file_path)

            # 记录统计结果
            self.logger.info(
                "帧数据分析完成，统计结果：\n"
                "  - 总帧数：%d\n"
                "  - 空闲帧数：%d\n"
                "  - 活动虚拟信道数：%d\n"
                "  - 不同译码状态数：%d",
                stats['total_frames'], stats['idle_frames'],
                stats['detailed_stats']['virtual_channel_stats']['active_channels'], 
                len(stats['decode_status'])
            )

            return stats

        except Exception as e:
            self.logger.error("标准帧分析失败: %s", str(e), exc_info=True)
            raise

    def _calculate_continuous_ratio(self, frame_counts: list) -> float:
        """计算帧连续性比率"""
        if not frame_counts:
            return 0.0

        continuous_count = 0
        total_frames = len(frame_counts)

        # 优化后的方法，避免大整数运算溢出
        for i in range(1, total_frames):
            current = frame_counts[i]
            previous = frame_counts[i - 1]
            
            # 计算期望的下一个值（考虑24位循环，最大值为0xFFFFFF）
            expected_next = previous + 1
            if expected_next > 0xFFFFFF:  # 如果超过24位最大值
                expected_next = 0  # 重置为0
            
            # 检查是否连续
            if current == expected_next:
                continuous_count += 1

        return (continuous_count / (total_frames - 1)) * 100 if total_frames > 1 else 100.0

    def _calculate_continuous_info(self, frames: list) -> dict:
        """计算帧连续性（按出现顺序，同一虚拟信道内判断）

        规则：
        1. 仅当上一帧和当前帧属于同一虚拟信道时才检查连续性。
        2. 连续性依据虚拟信道帧计数 24-bit 递增（溢出回 0）。
        3. 返回总体连续率及所有不连续帧的详细列表。
        """

        if not frames:
            return {
                'continuous_ratio': 0.0,
                'discontinuous_frames': [],
                'total_gaps': 0
            }

        # 统计器
        last_count_per_vc = {}
        last_index_per_vc = {}

        total_pairs = 0  # 同一 VC 连续对总数
        continuous_pairs = 0  # 连续成功对总数
        discontinuous_frames = []

        for idx, frame in enumerate(frames):
            vc = frame['virtual_channel']

            payload = frame.get('payload', b'')
            if len(payload) < 9:
                continue  # 无法取得帧计数

            curr_count = int.from_bytes(payload[6:9], byteorder='big')

            # 只有当上一帧就在文件中紧邻且属于同一 VC 时才检查连续性
            if vc in last_count_per_vc and last_index_per_vc[vc] == idx - 1:
                total_pairs += 1
                expected_next = (last_count_per_vc[vc] + 1) & 0xFFFFFF  # 24 位循环

                if curr_count == expected_next:
                    continuous_pairs += 1
                else:
                    # 记录不连续
                    gap_size = curr_count - expected_next if curr_count >= expected_next else (0xFFFFFF - expected_next + curr_count + 1)
                    discontinuous_frames.append({
                        'index': idx,
                        'previous_count': last_count_per_vc[vc],
                        'current_count': curr_count,
                        'expected_count': expected_next,
                        'gap_size': gap_size,
                        'virtual_channel': vc
                    })

            # 更新最近计数
            last_count_per_vc[vc] = curr_count
            last_index_per_vc[vc] = idx

        continuous_ratio = (continuous_pairs / total_pairs * 100) if total_pairs else 100.0

        # 控制记录数量，避免内存爆炸
        if len(discontinuous_frames) > 1000:
            discontinuous_frames = sorted(discontinuous_frames, key=lambda x: x['index'])[:1000]

        return {
            'continuous_ratio': continuous_ratio,
            'discontinuous_frames': discontinuous_frames,
            'total_gaps': len(discontinuous_frames)
        }

    def _get_sample_frame_counts(self, frames, max_samples=1000):
        """获取帧计数样本进行连续性分析
        
        注意：所有帧计数严格使用 payload[6:9]（与 test_analyze.py 一致），禁止使用 frame['frame_count'] 字段！
        对于大文件，分析所有帧的连续性会非常耗时，且意义不大
        此方法选择性地采样帧计数，确保性能
        
        Args:
            frames: 帧列表
            max_samples: 最大采样数量，默认1000
            
        Returns:
            list: 采样后的帧计数列表（虚拟信道帧计数）
        """
        total_frames = len(frames)
        
        # 如果帧数小于最大样本数，直接返回全部
        if total_frames <= max_samples:
            vc_counts = []
            for frame in frames:
                payload = frame.get('payload', b'')
                # 只允许用 payload[6:9] 作为帧计数
                vc_count = int.from_bytes(payload[6:9], byteorder='big') if len(payload) >= 9 else 0
                vc_counts.append(vc_count)
            return vc_counts
        
        # 否则进行等距采样
        sample_interval = total_frames // max_samples
        
        # 确保采样的帧具有代表性：前部、中部和尾部各采样1/3
        front_samples = frames[:max_samples//3]
        middle_start = (total_frames // 2) - (max_samples // 6)
        middle_end = middle_start + (max_samples // 3)
        middle_samples = frames[middle_start:middle_end]
        end_samples = frames[max(0, total_frames - max_samples//3):]
        
        # 合并采样结果并排序
        sampled_frames = front_samples + middle_samples + end_samples
        
        # 返回采样后的虚拟信道帧计数列表
        vc_counts = []
        for frame in sampled_frames:
            payload = frame.get('payload', b'')
            # 只允许用 payload[6:9] 作为帧计数
            vc_count = int.from_bytes(payload[6:9], byteorder='big') if len(payload) >= 9 else 0
            vc_counts.append(vc_count)
        return vc_counts

    def export_report(self, stats: dict, output_path: str):
        """导出统计分析报表"""
        try:
            if output_path.endswith('.csv'):
                # 导出为CSV格式
                # 1. 总体统计信息
                continuous_ratio = stats['detailed_stats']['frame_count_stats'].get('continuous_ratio', 0.0)
                summary_data = {
                    '项目': ['总帧数', '空闲帧数', '活动信道数', '帧连续性'],
                    '数值': [
                        stats['total_frames'],
                        stats['idle_frames'],
                        stats['detailed_stats']['virtual_channel_stats']['active_channels'],
                        f"{continuous_ratio:.2f}%"
                    ]
                }
                df_summary = pd.DataFrame(summary_data)
                df_summary.to_csv(output_path, index=False, encoding='utf-8-sig')

                # 2. 虚拟信道统计
                vc_data = []
                for vc, count in stats['virtual_channels'].items():
                    desc = stats['field_descriptions']['virtual_channels'].get(vc, str(vc))
                    counter = stats['vc_counters'].get(vc, 0)
                    ratio = (count / stats['total_frames']) * 100
                    vc_data.append({
                        '虚拟信道': f'VC{vc}',
                        '说明': desc,
                        '帧数量': count,
                        '占比': f'{ratio:.2f}%',
                        '计数器值': counter
                    })
                df_vc = pd.DataFrame(vc_data)
                df_vc.to_csv(output_path.replace('.csv', '_虚拟信道统计.csv'), index=False, encoding='utf-8-sig')

                # 3. 译码状态统计
                decode_data = []
                for status, count in stats['decode_status'].items():
                    desc = stats['field_descriptions']['decode_status'].get(status, str(status))
                    ratio = (count / stats['total_frames']) * 100
                    decode_data.append({
                        '译码状态': status,
                        '说明': desc,
                        '数量': count,
                        '占比': f'{ratio:.2f}%'
                    })
                df_decode = pd.DataFrame(decode_data)
                df_decode.to_csv(output_path.replace('.csv', '_译码状态统计.csv'), index=False, encoding='utf-8-sig')

                # 4. 相同帧计数文件列表
                files_data = []
                for frame_count, file_list in stats['frame_analysis']['frame_count_files'].items():
                    for file_path in file_list:
                        files_data.append({
                            '帧计数': frame_count,
                            '文件路径': file_path
                        })
                if files_data:
                    df_files = pd.DataFrame(files_data)
                    df_files.to_csv(output_path.replace('.csv', '_相同帧计数文件.csv'), index=False,
                                    encoding='utf-8-sig')
            else:
                # 创建Excel报表
                writer = pd.ExcelWriter(output_path, engine='xlsxwriter')

                # 1. 总体统计信息
                continuous_ratio = stats['detailed_stats']['frame_count_stats'].get('continuous_ratio', 0.0)
                summary_data = {
                    '项目': ['总帧数', '空闲帧数', '活动信道数', '帧连续性'],
                    '数值': [
                        stats['total_frames'],
                        stats['idle_frames'],
                        stats['detailed_stats']['virtual_channel_stats']['active_channels'],
                        f"{continuous_ratio:.2f}%"
                    ]
                }
                pd.DataFrame(summary_data).to_excel(writer, sheet_name='总体统计', index=False)

                # 2. 虚拟信道统计
                vc_data = []
                for vc, count in stats['virtual_channels'].items():
                    desc = stats['field_descriptions']['virtual_channels'].get(vc, str(vc))
                    counter = stats['vc_counters'].get(vc, 0)
                    ratio = (count / stats['total_frames']) * 100
                    vc_data.append({
                        '虚拟信道': f'VC{vc}',
                        '说明': desc,
                        '帧数量': count,
                        '占比': f'{ratio:.2f}%',
                        '计数器值': counter
                    })
                pd.DataFrame(vc_data).to_excel(writer, sheet_name='虚拟信道统计', index=False)

                # 3. 译码状态统计
                decode_data = []
                for status, count in stats['decode_status'].items():
                    desc = stats['field_descriptions']['decode_status'].get(status, str(status))
                    ratio = (count / stats['total_frames']) * 100
                    decode_data.append({
                        '译码状态': status,
                        '说明': desc,
                        '数量': count,
                        '占比': f'{ratio:.2f}%'
                    })
                pd.DataFrame(decode_data).to_excel(writer, sheet_name='译码状态统计', index=False)

                # 4. 相同帧计数文件列表
                files_data = []
                for frame_count, file_list in stats['frame_analysis']['frame_count_files'].items():
                    for file_path in file_list:
                        files_data.append({
                            '帧计数': frame_count,
                            '文件路径': file_path
                        })
                if files_data:
                    pd.DataFrame(files_data).to_excel(writer, sheet_name='相同帧计数文件', index=False)

                writer.close()

            self.logger.info(f"统计报表已导出到: {output_path}")

        except Exception as e:
            self.logger.error(f"导出统计报表时发生错误: {str(e)}")
            raise

    def compare_files(self, file1_path: str, file2_path: str) -> dict:
        """比对两个文件
        
        Args:
            file1_path: 第一个文件路径（基准文件）
            file2_path: 第二个文件路径（比对文件）
        """
        try:
            self.logger.info(f"开始比对文件: {file1_path} 和 {file2_path}")

            # 读取文件
            frames1 = self.read_frame(file1_path)
            frames2 = self.read_frame(file2_path)

            # 按范围比对帧
            comparison = self.compare_frames_by_range(frames1, frames2)

            self.logger.info("文件比对完成")
            return comparison

        except Exception as e:
            self.logger.error(f"比对文件失败: {str(e)}")
            raise Exception(f"比对文件失败: {str(e)}")

    def compare_frames_by_range(self, frames1: list, frames2: list, start_count: int = None,
                                end_count: int = None) -> dict:
        """按范围比对帧数据
        
        Args:
            frames1: 第一个文件的帧列表
            frames2: 第二个文件的帧列表
            start_count: 起始帧计数（可选）
            end_count: 结束帧计数（可选）
        """
        try:
            # 创建比对结果字典
            comparison = {
                'total_frames': (len(frames1), len(frames2)),
                'matching_frames': 0,
                'mismatches': [],
                'frame_results': []
            }

            # 过滤帧计数范围
            frames1_filtered = [f for f in frames1 if
                                (start_count is None or int(f['frame_count']) >= start_count) and
                                (end_count is None or int(f['frame_count']) <= end_count) and
                                int(f['frame_count']) != 0]  # 排除帧计数为0的帧
            frames2_filtered = [f for f in frames2 if
                                (start_count is None or int(f['frame_count']) >= start_count) and
                                (end_count is None or int(f['frame_count']) <= end_count) and
                                int(f['frame_count']) != 0]  # 排除帧计数为0的帧

            # 创建帧计数索引 - 使用Python int类型作为键
            frames1_dict = {int(f['frame_count']): f for f in frames1_filtered}
            frames2_dict = {int(f['frame_count']): f for f in frames2_filtered}

            # 获取所有帧计数
            max_frames = max(len(frames1_filtered), len(frames2_filtered))

            # 比对每个帧
            for i in range(max_frames):
                frame1 = frames1_filtered[i] if i < len(frames1_filtered) else None
                frame2 = frames2_filtered[i] if i < len(frames2_filtered) else None

                count = int(frame1['frame_count']) if frame1 else int(frame2['frame_count'])

                # 创建当前帧的比对结果
                frame_result = {
                    'frame_count': count,
                    'exists_in_file1': frame1 is not None,
                    'exists_in_file2': frame2 is not None,
                    'is_matching': False,
                    'differences': []
                }

                # 检查帧是否缺失
                if frame1 is None or frame2 is None:
                    frame_result['type'] = 'missing_frame'
                    frame_result['missing_in'] = '1' if frame1 is None else '2'
                    comparison['mismatches'].append({
                        'frame_count': count,
                        'type': 'missing_frame',
                        'missing_in': '1' if frame1 is None else '2'
                    })
                else:
                    # 比对帧内容
                    differences = []

                    # 比对数据负载
                    # 注意：NumPy数组不能直接比较，需要使用np.array_equal或转换后比较
                    payload_equal = np.array_equal(frame1['payload'], frame2['payload']) if isinstance(frame1['payload'], np.ndarray) else frame1['payload'] == frame2['payload']
                    
                    if not payload_equal:
                        byte_differences = []

                        # 根据帧长度判断比对区域
                        # 如果payload长度大于1024字节，说明是1092字节的帧，只比对前1024字节
                        # 如果payload长度等于1024字节，直接比对全部数据
                        payload1 = frame1['payload']
                        payload2 = frame2['payload']

                        if len(payload1) > 1024 and len(payload2) > 1024:
                            # 只比对前1024字节数据区
                            payload1 = payload1[:1024]
                            payload2 = payload2[:1024]

                        min_len = min(len(payload1), len(payload2))

                        for i in range(min_len):
                            try:
                                # 处理NumPy数组和普通字节的情况
                                if hasattr(payload1[i], 'item'):  # NumPy标量
                                    b1 = int(payload1[i])
                                else:  # 普通字节
                                    b1 = int.from_bytes(payload1[i:i + 1], byteorder='big')
                                    
                                if hasattr(payload2[i], 'item'):  # NumPy标量
                                    b2 = int(payload2[i])
                                else:  # 普通字节
                                    b2 = int.from_bytes(payload2[i:i + 1], byteorder='big')
                                    
                                if b1 != b2:
                                    byte_differences.append({
                                        'byte_position': i,
                                        'byte1': b1,
                                        'byte2': b2,
                                        'diff_bits': bin(b1 ^ b2)[2:].zfill(8)
                                    })
                            except Exception as e:
                                self.logger.error(f"处理字节差异时出错: {str(e)}")
                                continue

                        if byte_differences:
                            differences.append({
                                'field': 'payload',
                                'differences': byte_differences
                            })

                    if differences:
                        frame_result['type'] = 'content_mismatch'
                        frame_result['is_matching'] = False
                        frame_result['differences'] = differences
                        comparison['mismatches'].append({
                            'frame_count': count,
                            'type': 'content_mismatch',
                            'differences': differences
                        })
                    else:
                        frame_result['type'] = 'matching'
                        frame_result['is_matching'] = True
                        comparison['matching_frames'] += 1

                # 添加当前帧的比对结果
                comparison['frame_results'].append(frame_result)

            # 计算统计信息
            total_frames = max_frames
            comparison['statistics'] = {
                'match_ratio': (comparison['matching_frames'] / total_frames * 100) if total_frames > 0 else 0,
                'mismatch_ratio': (len(comparison['mismatches']) / total_frames * 100) if total_frames > 0 else 0,
                'total_differences': len(comparison['mismatches']),
                'by_type': {
                    'missing_frames': len([m for m in comparison['mismatches'] if m['type'] == 'missing_frame']),
                    'content_mismatches': len([m for m in comparison['mismatches'] if m['type'] == 'content_mismatch'])
                },
                'frame_stats': {
                    'total_frames': total_frames,
                    'frames_in_file1': len(frames1_filtered),
                    'frames_in_file2': len(frames2_filtered),
                    'matching_frames': comparison['matching_frames'],
                    'missing_frames': len([r for r in comparison['frame_results'] if r['type'] == 'missing_frame']),
                    'mismatched_frames': len(
                        [r for r in comparison['frame_results'] if r['type'] == 'content_mismatch'])
                }
            }

            return comparison

        except Exception as e:
            self.logger.error(f"比对帧数据时发生错误: {str(e)}")
            raise Exception(f"比对文件失败: {str(e)}")

    def save_comparison_result(self, comparison: dict, output_format: str = 'excel', output_path: str = None,
                               db_config: dict = None, message_queue: Queue = None):
        """保存比对结果
        
        Args:
            comparison: 比对结果字典
            output_format: 输出格式，支持 'excel', 'db', 'message'
            output_path: 输出文件路径（用于excel格式）
            db_config: 数据库配置（用于db格式）
            message_queue: 消息队列（用于message格式）
        """
        try:
            if output_format == 'excel':
                if output_path is None:
                    output_path = f"comparison_result_{time.strftime('%Y%m%d_%H%M%S')}.xlsx"
                self.export_comparison_report(comparison, output_path)

            elif output_format == 'db':
                if db_config is None:
                    raise ValueError("数据库配置不能为空")
                self._save_to_database(comparison, db_config)

            elif output_format == 'message':
                if message_queue is None:
                    raise ValueError("消息队列不能为空")
                message_queue.put({
                    'type': 'comparison_result',
                    'data': comparison,
                    'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
                })

            else:
                raise ValueError(f"不支持的输出格式: {output_format}")

            self.logger.info(f"比对结果已保存为{output_format}格式")

        except Exception as e:
            self.logger.error(f"保存比对结果时发生错误: {str(e)}")
            raise

    def _save_to_database(self, comparison: dict, db_config: dict):
        """将比对结果保存到数据库"""
        try:
            import sqlite3  # 这里使用SQLite作为示例，可以根据需要替换为其他数据库

            conn = sqlite3.connect(db_config.get('database', 'frame_comparison.db'))
            cursor = conn.cursor()

            # 创建比对结果表
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS comparison_results (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    timestamp TEXT,
                    total_frames_1 INTEGER,
                    total_frames_2 INTEGER,
                    matching_frames INTEGER,
                    total_differences INTEGER,
                    match_ratio REAL,
                    compare_modes TEXT,
                    frame_range TEXT,
                    result_data TEXT
                )
            ''')

            # 插入比对结果
            cursor.execute('''
                INSERT INTO comparison_results (
                    timestamp, total_frames_1, total_frames_2, matching_frames,
                    total_differences, match_ratio, compare_modes, frame_range, result_data
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            ''', (
                comparison.get('timestamp', time.strftime('%Y-%m-%d %H:%M:%S')),
                comparison['total_frames'][0],
                comparison['total_frames'][1],
                comparison['matching_frames'],
                comparison['statistics']['total_differences'],
                comparison['statistics']['match_ratio'],
                ','.join(comparison.get('compare_modes', [])),
                str(comparison.get('frame_range', {})),
                str(comparison)  # 将完整结果作为文本存储
            ))

            conn.commit()
            conn.close()

        except Exception as e:
            self.logger.error(f"保存到数据库时发生错误: {str(e)}")
            raise

    def parse_frame_data(self, frame_data: bytes) -> dict:
        """解析帧数据，基于用户提供的帧格式描述"""
        try:
            frame = {}
            self.logger.info(f"开始解析帧数据，数据长度：{len(frame_data)}字节")

            # 解析帧头
            frame['header'] = struct.unpack('>I', frame_data[0:4])[0]
            self.logger.debug(f"帧头：0x{frame['header']:08X}")
            if frame['header'] != 1234567890:
                self.logger.warning(f"帧头不是预期的固定值1234567890，实际值: {frame['header']}")

            # 解析全帧长度
            frame['full_length'] = struct.unpack('>I', frame_data[4:8])[0]
            self.logger.debug(f"全帧长度：{frame['full_length']}字节")
            if frame['full_length'] != 1092:
                self.logger.warning(f"全帧长度不是预期的固定值1092，实际值: {frame['full_length']}")

            # 解析保留字节1
            frame['reserved_1'] = struct.unpack('>I', frame_data[8:12])[0]
            self.logger.debug(f"保留字节1：0x{frame['reserved_1']:08X}")

            # 解析时间标签
            frame['time_label'] = struct.unpack('>I', frame_data[12:16])[0]
            self.logger.debug(f"时间标签：{frame['time_label']} (UTC秒计数)")

            # 解析毫秒计数
            frame['milliseconds'] = struct.unpack('>I', frame_data[16:20])[0]
            self.logger.debug(f"毫秒计数：{frame['milliseconds']}")

            # 解析帧计数
            frame['frame_count'] = struct.unpack('>I', frame_data[20:24])[0]
            self.logger.debug(f"帧计数：{frame['frame_count']}")

            # 解析帧检验结果
            frame['check_result'] = struct.unpack('>I', frame_data[24:28])[0]
            self.logger.debug(f"帧检验结果：0x{frame['check_result']:08X}")

            # 解析帧同步状态
            frame['sync_status'] = struct.unpack('>I', frame_data[28:32])[0]
            self.logger.debug(f"帧同步状态：0x{frame['sync_status']:08X}")

            # 解析位滑状态
            frame['bit_slip_status'] = struct.unpack('>I', frame_data[32:36])[0]
            self.logger.debug(f"位滑状态：0x{frame['bit_slip_status']:08X}")

            # 解析保留字节2
            frame['reserved_2'] = struct.unpack('>I', frame_data[36:40])[0]
            self.logger.debug(f"保留字节2：0x{frame['reserved_2']:08X}")

            # 解析帧长度
            frame['frame_length'] = struct.unpack('>I', frame_data[40:44])[0]
            self.logger.debug(f"帧长度：{frame['frame_length']}字节")

            # 解析同步字长度
            frame['sync_word_length'] = struct.unpack('>I', frame_data[44:48])[0]
            self.logger.debug(f"同步字长度：{frame['sync_word_length']}位")

            # 解析帧检验&译码状态
            frame['check_decode_status'] = struct.unpack('>I', frame_data[48:52])[0]
            self.logger.debug(f"帧检验&译码状态：0x{frame['check_decode_status']:08X}")

            # 解析帧校验及同步字状态
            frame['check_sync_status'] = struct.unpack('>I', frame_data[52:56])[0]
            self.logger.debug(f"帧校验及同步字状态：0x{frame['check_sync_status']:08X}")

            # 解析保留字节3和4
            frame['reserved_3'] = struct.unpack('>I', frame_data[56:60])[0]
            frame['reserved_4'] = struct.unpack('>I', frame_data[60:64])[0]
            self.logger.debug(f"保留字节3：0x{frame['reserved_3']:08X}")
            self.logger.debug(f"保留字节4：0x{frame['reserved_4']:08X}")

            # 解析VCDU数据区
            frame['vcdu_data'] = frame_data[64:1088]
            self.logger.debug(f"VCDU数据区长度：{len(frame['vcdu_data'])}字节")

            # 解析帧尾
            frame['footer'] = struct.unpack('>i', frame_data[1088:1092])[0]
            self.logger.debug(f"帧尾：{frame['footer']}")

            # 记录完整的帧解析结果
            self.logger.info(
                f"帧解析完成，关键信息：\n"
                f"  - 帧计数：{frame['frame_count']}\n"
                f"  - 时间标签：{frame['time_label']} UTC，{frame['milliseconds']}毫秒\n"
                f"  - 帧长度：{frame['frame_length']}字节\n"
                f"  - 同步状态：0x{frame['sync_status']:08X}\n"
                f"  - 位滑状态：0x{frame['bit_slip_status']:08X}\n"
                f"  - 译码状态：0x{frame['check_decode_status']:08X}"
            )

            return frame

        except Exception as e:
            self.logger.error(f"解析帧数据失败: {str(e)}", exc_info=True)
            raise

    def analyze_frame_error_rate(self, frames: list, ref_frame_index: int, compare_ranges: list) -> dict:
        """分析帧误码率
        
        Args:
            frames: 所有帧的列表
            ref_frame_index: 标准帧的索引
            compare_ranges: 需要比对的字节范围列表，例如 [(138, 1023)] 表示比对数据区
            
        Returns:
            分析结果字典
        """
        try:
            if not frames or ref_frame_index >= len(frames):
                raise ValueError("无效的标准帧索引")

            # 获取标准帧
            ref_frame = frames[ref_frame_index]

            # 初始化结果
            results = {
                'total_frames': len(frames),
                'compared_frames': len(frames) - 1,  # 不包括标准帧自身
                'error_frames': 0,
                'total_error_rate': 0.0,
                'frame_results': []
            }

            total_error_bytes = 0
            total_compared_bytes = 0

            # 遍历所有帧（除了标准帧）进行比对
            for i, frame in enumerate(frames):
                if i == ref_frame_index:
                    continue

                # 初始化当前帧的比对结果
                frame_result = {
                    'frame_index': i,
                    'error_bytes': 0,
                    'error_positions': [],
                    'error_rate': 0.0
                }

                # 比对每个指定范围的字节
                for start, end in compare_ranges:
                    # 确保不超出数据范围
                    end = min(end, len(frame['payload']) - 1, len(ref_frame['payload']) - 1)
                    if start > end:
                        continue

                    # 比对指定范围的字节
                    for pos in range(start, end + 1):
                        try:
                            ref_byte = int.from_bytes(ref_frame['payload'][pos:pos + 1], byteorder='big')
                            comp_byte = int.from_bytes(frame['payload'][pos:pos + 1], byteorder='big')

                            if ref_byte != comp_byte:
                                frame_result['error_bytes'] += 1
                                frame_result['error_positions'].append({
                                    'position': pos,
                                    'ref_value': hex(ref_byte),
                                    'comp_value': hex(comp_byte),
                                    'diff_bits': bin(ref_byte ^ comp_byte)[2:].zfill(8)
                                })

                        except Exception as e:
                            self.logger.error(f"比对字节时出错: {str(e)}")
                            continue

                    # 更新总字节数
                    total_compared_bytes += (end - start + 1)

                # 计算当前帧的误码率
                bytes_compared = sum(end - start + 1 for start, end in compare_ranges)
                frame_result['error_rate'] = frame_result['error_bytes'] / bytes_compared if bytes_compared > 0 else 0.0

                # 更新总误码字节数
                total_error_bytes += frame_result['error_bytes']

                # 如果有误码，增加误码帧计数
                if frame_result['error_bytes'] > 0:
                    results['error_frames'] += 1

                # 添加当前帧的结果
                results['frame_results'].append(frame_result)

            # 计算总体误码率
            results['total_error_rate'] = total_error_bytes / total_compared_bytes if total_compared_bytes > 0 else 0.0

            return results

        except Exception as e:
            self.logger.error(f"分析帧误码率时发生错误: {str(e)}")
            raise

    def compare_frames(self, frames1: list, frames2: list, file1_path: str, file2_path: str,
                       compare_range: tuple = None) -> dict:
        """比对两个帧列表中的数据
        
        Args:
            frames1: 第一个文件的帧列表
            frames2: 第二个文件的帧列表
            file1_path: 第一个文件路径
            file2_path: 第二个文件路径
            compare_range: 比对范围元组 (start, end)，例如 (0, 1091) 表示比对全帧，(68, 1091) 表示比对VCDU数据区
            
        Returns:
            比对结果字典
        """
        try:
            # 初始化比对结果
            comparison = {
                'total_frames': [len(frames1), len(frames2)],
                'frame_results': [],
                'matching_frames': 0,
                'frames1': frames1,  # 添加原始帧数据
                'frames2': frames2   # 添加原始帧数据
            }

            # 创建帧计数到帧的映射 - 确保使用Python的int类型作为键
            frames1_dict = {int(frame['frame_count']): frame for frame in frames1}
            frames2_dict = {int(frame['frame_count']): frame for frame in frames2}

            # 遍历所有共同的帧计数
            common_frame_counts = sorted(set(frames1_dict.keys()) & set(frames2_dict.keys()))
            self.logger.info(f"开始比对 {len(common_frame_counts)} 个共同帧计数")

            # 定义报告差异数量的限制
            MAX_DIFF_REPORTS = 100

            for idx, frame_count in enumerate(common_frame_counts):
                if idx % 1000 == 0: # 每处理1000帧记录一次日志
                    self.logger.debug(f"正在比对帧计数 {frame_count} (进度 {idx}/{len(common_frame_counts)}) ")

                frame1 = frames1_dict[frame_count]
                frame2 = frames2_dict[frame_count]

                # 比较帧内容
                differences = []

                # 比较虚拟信道
                if frame1['virtual_channel'] != frame2['virtual_channel']:
                    differences.append({
                        'field': 'virtual_channel',
                        'value1': int(frame1['virtual_channel']),
                        'value2': int(frame2['virtual_channel'])
                    })

                # 比较译码状态
                if frame1['decode_status'] != frame2['decode_status']:
                    differences.append({
                        'field': 'decode_status',
                        'value1': int(frame1['decode_status']),
                        'value2': int(frame2['decode_status'])
                    })

                # 比较负载数据 (使用NumPy优化)
                payload1 = frame1['payload']
                payload2 = frame2['payload']

                # 确保payload是NumPy数组
                if isinstance(payload1, bytes):
                    payload1 = np.frombuffer(payload1, dtype=np.uint8)
                if isinstance(payload2, bytes):
                    payload2 = np.frombuffer(payload2, dtype=np.uint8)

                # 检查长度是否一致
                if len(payload1) != len(payload2):
                    differences.append({
                        'field': 'payload',
                        'differences': [{
                            'type': 'length_mismatch',
                            'length1': len(payload1),
                            'length2': len(payload2)
                        }]
                    })
                else:
                    # 根据比对范围参数确定比对区域
                    min_len = len(payload1) # 长度已经确认一致
                    start_pos = compare_range[0] if compare_range else 0
                    # 确保结束位置不超过实际长度
                    end_pos = min(compare_range[1] if compare_range else min_len - 1, min_len - 1)

                    # 只有在范围有效时才进行比较
                    if start_pos <= end_pos:
                        # 切片获取比较区域
                        sliced_payload1 = payload1[start_pos : end_pos + 1]
                        sliced_payload2 = payload2[start_pos : end_pos + 1]

                        # 使用NumPy进行快速比较
                        if not np.array_equal(sliced_payload1, sliced_payload2):
                            payload_diffs = []
                            # 使用np.where找到所有差异的索引 (相对于切片后的数组)
                            diff_indices = np.where(sliced_payload1 != sliced_payload2)[0]

                            # 限制报告的差异数量
                            reported_diff_count = 0
                            for index in diff_indices:
                                if reported_diff_count >= MAX_DIFF_REPORTS:
                                    payload_diffs.append({
                                        'type': 'too_many_diffs',
                                        'limit': MAX_DIFF_REPORTS
                                    })
                                    break
                                    
                                payload_diffs.append({
                                    'byte_position': start_pos + int(index), # 转换回原始payload中的位置
                                    'value1': int(sliced_payload1[index]),
                                    'value2': int(sliced_payload2[index]),
                                    'diff_bits': bin(int(sliced_payload1[index]) ^ int(sliced_payload2[index]))[2:].zfill(8)  # 添加位差异信息
                                })
                                reported_diff_count += 1
                                
                            if payload_diffs: # 只有在找到差异时才添加
                                differences.append({
                                    'field': 'payload',
                                    'differences': payload_diffs
                                })
                    else:
                        self.logger.warning(f"帧 {frame_count}: 无效的比对范围 [{start_pos}, {end_pos}]，跳过payload比对")


                # 添加比对结果
                if differences:
                    comparison['frame_results'].append({
                        'frame_count': int(frame_count),
                        'type': 'content_mismatch',
                        'differences': differences
                    })
                else:
                    comparison['frame_results'].append({
                        'frame_count': int(frame_count),
                        'type': 'matching'
                    })
                    comparison['matching_frames'] += 1

            self.logger.info(f"比对完成，匹配帧数: {comparison['matching_frames']}/{len(common_frame_counts)}")
            return comparison

        except Exception as e:
            self.logger.error(f"比对帧数据失败: {str(e)}", exc_info=True)
            raise

    @staticmethod
    def extract_virtual_channel_frame_count(frame):
        """从帧数据中提取虚拟信道帧计数
        
        Args:
            frame: 帧数据字典，包含payload等字段
            
        Returns:
            int: 虚拟信道帧计数值（24位），如果提取失败返回0
        """
        try:
            if not isinstance(frame, dict) or 'payload' not in frame:
                return 0
                
            payload = frame['payload']
            
            # 根据payload长度判断帧类型并提取虚拟信道帧计数
            if len(payload) >= 1024:  # 1092字节帧的VCDU数据区（1024字节）
                # 1092字节帧：payload现在是完整的VCDU数据区，虚拟信道帧计数在第6-8字节
                # 这对应原始1092字节帧的第70-72字节（64+6=70）
                if len(payload) >= 9:
                    vc_count = int.from_bytes(payload[6:9], byteorder='big')
                    return vc_count
            elif len(payload) >= 896:  # 1024或896字节帧
                # 1024/896字节帧：虚拟信道帧计数在第6-8字节
                if len(payload) >= 9:
                    vc_count = int.from_bytes(payload[6:9], byteorder='big')
                    return vc_count
            else:
                # 其他格式，尝试从第6-8字节提取
                if len(payload) >= 9:
                    vc_count = int.from_bytes(payload[6:9], byteorder='big')
                    return vc_count
                    
            return 0
            
        except Exception:
            # 提取失败时返回0
            return 0
